package main

import (
	"context"
	"f5-tool-setup-cni/cnisetup"
	"flag"
	"fmt"
	"os"

	f5_bigip "github.com/f5devcentral/f5-bigip-rest-go/bigip"
	"github.com/f5devcentral/f5-bigip-rest-go/utils"
	"github.com/google/uuid"
)

func main() {
	var (
		configPath string
		loglevel   string
	)
	flag.StringVar(&configPath, "config", "./config.yaml", "BIG-IP configuration yaml file.")
	flag.StringVar(&loglevel, "log-level", "info", "logging level: debug, info, warn, error, critical")
	flag.Parse()

	configs, err := cnisetup.LoadConfigsFromFile(configPath)
	if err != nil {
		fmt.Println(err.Error())
		os.Exit(1)
	}

	for _, ncniconf := range *configs {
		ctx := newContext(loglevel)
		slog := utils.LogFromContext(ctx)
		ncnictx := cnisetup.CNIContext{Context: ctx, CNIConfig: ncniconf}
		bc := ncnictx.NewBC()

		if err := rmOriginal(bc); err != nil {
			slog.Errorf("failed to remove original config on bigip [%s]: %s",
				ncnictx.Mgmt.IpAddress, err.Error())
			os.Exit(1)
		} else {
			slog.Infof("successfully removed original configs from bigip [%s]",
				ncnictx.Mgmt.IpAddress)
		}

		slog.Debugf(ncnictx.Dump())
		if err := newTarget(&ncnictx); err != nil {
			slog.Errorf("failed to create target config to bigip [%s]: %s",
				ncnictx.Mgmt.IpAddress, err.Error())
			os.Exit(1)
		} else {
			slog.Infof("successfully created configurations to bigip [%s]",
				ncnictx.Mgmt.IpAddress)
		}
	}
}

func newContext(loglevel string) context.Context {
	reqid := uuid.New().String()
	slog := utils.NewLog().WithLevel(loglevel).WithRequestID(reqid)
	ctxid := context.WithValue(context.TODO(), utils.CtxKey_RequestID, reqid)
	ctx := context.WithValue(ctxid, utils.CtxKey_Logger, slog)
	return ctx
}

func rmOriginal(bc *f5_bigip.BIGIPContext) error {
	slog := utils.LogFromContext(bc)

	ocniconf, err := cnisetup.LoadConfigFromDataGroup(bc)
	if err != nil {
		return fmt.Errorf("failed to get config from bigip: %s", err.Error())
	}
	if ocniconf == nil {
		slog.Infof("skip deleting old cni configuration")
		return nil
	}
	slog.Infof("loaded old cni configuration from %s", cnisetup.CNIConfigDGName)

	ocnictx := cnisetup.CNIContext{CNIConfig: *ocniconf, Context: bc.Context}
	if err := ocnictx.Cleanup(); err != nil {
		return fmt.Errorf("failed to clean old config: %s", err.Error())
	}

	for _, kube := range ocnictx.Kubes {
		ok8scni := ocnictx.NewK8SCNI(kube)

		if err := ok8scni.Teardown(); err != nil {
			return err
		}
	}

	if err := bc.DeleteDataGroup(cnisetup.CNIConfigDGName, "Common"); err != nil {
		return err
	}
	return nil
}

func newTarget(cnictx *cnisetup.CNIContext) error {
	bc := cnictx.NewBC()

	if err := cnictx.Deploy(); err != nil {
		return err
	}

	for _, kube := range cnictx.Kubes {
		k8scni := cnictx.NewK8SCNI(kube)
		if err := k8scni.Setup(); err != nil {
			return err
		}
	}

	if err := bc.SaveDataGroup(cnisetup.CNIConfigDGName, "Common", []byte(cnictx.Dump())); err != nil {
		return err
	}
	return nil
}
